home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1999 June: Reference Library / Dev.CD Jun 99 RL Disk 1.toast / Technical Documentation / Develop / develop Issue 22 / develop Issue 22 code / PCI Driver Sample.sea / PCI Driver Sample / NCR_DriverProject / Src / NCRStartScript.c / NCRStartScript.c
Encoding:
C/C++ Source or Header  |  1995-07-24  |  14.7 KB  |  428 lines  |  [TEXT/MPCC]

  1. /*                                    NCRStartScript.c                                */
  2. /*
  3.  * NCRStartScript.c
  4.  * Copyright © 1994 Apple Computer Inc. All rights reserved.
  5.  */
  6. /*    .___________________________________________________________________________________.
  7.       | These low-level routines convert an I/O request into a form that can be handled    |
  8.       | by the NCR hardware. While this is primarily specific to the NCR chip, you should    |
  9.       | understand how the setup functions call PrepareMemoryForIO. This is a limited        |
  10.       | sample that does not handle scatter-gather I/O requests -- these will be needed    |
  11.       | in a production driver. However, they require a somewhat more complex NCR script;    |
  12.       | or, at the very least, more thought on my part.                                    |
  13.     .___________________________________________________________________________________.
  14. */
  15. #include "NCRDriverPrivate.h"
  16. #include <stddef.h>
  17.  
  18. OSErr                        NCRSetupDoSCSIScript(
  19.         PerRequestDataPtr        perRequestDataPtr
  20.     );
  21. ByteCount                    SetupSCSICommand(
  22.         PerRequestDataPtr        perRequestDataPtr
  23.     );
  24. ByteCount                    SCSIGetCommandLength(
  25.         const unsigned char        *cmdBlock
  26.     );
  27. OSErr                        NCRSetupTestISR(
  28.         PerRequestDataPtr        perRequestDataPtr
  29.     );
  30. OSErr                        NCRSetupTestMemory(
  31.         PerRequestDataPtr        perRequestDataPtr
  32.     );
  33.  
  34. /*
  35.  * All functions here use a standard variable set to access information.
  36.  * I've "globalized" this to minimize clutter.
  37.  */
  38. #define REQUEST            (*perRequestDataPtr)
  39. #define PB                (*((IOParam *) REQUEST.pb))
  40. #define SCSI            (*((NCRSCSIParamPtr) PB.ioMisc))
  41. #define SCRIPT            (REQUEST.scriptData)
  42. /*
  43.  * AddressOf is a physical address
  44.  */
  45. #define AddressOf(what)    (((UInt32) REQUEST.scriptDataPtr) + offsetof(ScriptData, what))
  46. /*
  47.  * SetTable (must be a macro) stores an address and byte count into a table.
  48.  */
  49. #define SetTable(whichTable, physAddress, dataCount) do {            \
  50.         SCRIPT.whichTable.address = EndianSwap32Bit((UInt32) physAddress);    \
  51.         SCRIPT.whichTable.byteCount = EndianSwap32Bit(dataCount);            \
  52.     } while (0)
  53.  
  54. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  55.  * NCRStartScript starts a Script operation. The script completes through a Secondary
  56.  * Interrupt Handler.
  57.  */
  58. OSErr
  59. NCRStartScript(
  60.         AddressSpaceID            addressSpaceID,
  61.         IOCommandID                ioCommandID,
  62.         ParmBlkPtr                pb,
  63.         ScriptSelector            scriptSelector
  64.     )
  65. {
  66.         OSErr                    status;
  67.         PerRequestDataPtr        perRequestDataPtr;
  68.  
  69.         Trace(NCRStartScript);
  70.         /*
  71.          * When we support concurrent transactions, we'll get the current
  72.          * PerRequestData record from a pool. If there aren't any available,
  73.          * we'll hide this parameter block on a look-aside queue and return
  74.          * "in progress".
  75.          */
  76.         perRequestDataPtr = GLOBAL.perRequestDataPtr;
  77.         /*
  78.          * Clear out the volatile parts of the table.
  79.          */
  80.         CLEAR(REQUEST.scriptData);                /* Shared with the NCR chip        */
  81.         CLEAR(REQUEST.shadow);                    /* Current interrupt state        */
  82.         REQUEST.timerID = kInvalidID;            /* No watchdog timer id yet        */
  83.         REQUEST.addressSpaceID = addressSpaceID;
  84.         REQUEST.pb = pb;
  85.         REQUEST.ioCommandID = ioCommandID;
  86. #if USE_LOG_LIBRARY
  87.         REQUEST.prepareMemoryCount = 0;            /* Number of times we call it    */
  88.         REQUEST.deviceWaitCount = 0;            /* Times we start the device    */
  89. #endif
  90.         if (scriptSelector == kSCSICommandScript
  91.          && SCSI.targetID == kNCRMemoryTestBusID) {
  92.             scriptSelector = (SCSI.memTestPhysAddress == NULL)
  93.                         ? kSCSITestISRScript : kSCSITestMemoryScript;
  94.         }
  95.         REQUEST.scriptSelector = scriptSelector;
  96.         switch (scriptSelector) {
  97.         case kBusResetScript:
  98.             REQUEST.scriptPtr =
  99.                         ((UInt32) GLOBAL.scriptIOTable.physicalMapping[0])
  100.                         + kBusResetScript;
  101.             REQUEST.scriptBaseAddress =
  102.                         (UInt32) GLOBAL.scriptIOTable.physicalMapping[0];
  103.             REQUEST.scriptDataPtr = kInvalidPageAddress;
  104.             REQUEST.watchdogTimeout = kNoSCSITimeout; /* We have a standard timeout    */
  105.             status = noErr;
  106.             break;
  107.         case kSCSICommandScript:
  108.             REQUEST.watchdogTimeout = SCSI.watchdogTimeout;
  109.             REQUEST.scriptPtr =
  110.                         ((UInt32) GLOBAL.scriptIOTable.physicalMapping[0])
  111.                         + kSCSICommandScript;
  112.             REQUEST.scriptDataPtr =
  113.                         ((UInt32) REQUEST.perRequestIOTable.physicalMapping[0])
  114.                         + offsetof(PerRequestData, scriptData);
  115.             status = NCRSetupDoSCSIScript(perRequestDataPtr);
  116.             REQUEST.scriptBaseAddress =
  117.                         (UInt32) GLOBAL.scriptIOTable.physicalMapping[0];
  118.             break;
  119.         case kSCSIRundownScript:
  120.             status = NCRBusBusyCheck();
  121.             if (status == noErr) {
  122.                 REQUEST.watchdogTimeout =
  123.                     ((NCRDriverRundownParamPtr) pb)->watchdogTimeout;
  124.                 REQUEST.scriptPtr =
  125.                         ((UInt32) GLOBAL.scriptIOTable.physicalMapping[0])
  126.                         + kSCSIRundownScript;
  127.                 REQUEST.scriptDataPtr =
  128.                             ((UInt32) REQUEST.perRequestIOTable.physicalMapping[0])
  129.                             + offsetof(PerRequestData, scriptData);
  130.                 REQUEST.scriptBaseAddress =
  131.                             (UInt32) GLOBAL.scriptIOTable.physicalMapping[0];
  132.                 SCRIPT.idMsgByte = 0x0;                /* Bogus id                    */
  133.                 SCRIPT.statusByte = 0xFF;            /* Initialize to garbage    */
  134.                 SCRIPT.commandCompleteByte = 0xFF;    /* Initialize to garbage    */
  135.                 /*
  136.                  * Setup the scatter-gather tables.
  137.                  */
  138.                 SetTable(deviceIDTable,        0,                                0);
  139.                 SetTable(idMsgTable,        AddressOf(idMsgByte),            1);
  140.                 SetTable(commandTable,        AddressOf(scsiCommand),            0);
  141.                 SetTable(statusTable,        AddressOf(statusByte),            1);
  142.                 SetTable(completeTable,        AddressOf(commandCompleteByte),    1);
  143.                 SetTable(bitBucketInTable,    AddressOf(bitBucketInByte),        1);
  144.                 SetTable(bitBucketOutTable,    AddressOf(bitBucketOutByte),    1);
  145.                 SetTable(ignoredMsgTable,    AddressOf(ignoredMsgInByte),    1);
  146.             }
  147.             break;
  148.         case kSCSITestISRScript:
  149.             REQUEST.watchdogTimeout = SCSI.watchdogTimeout;
  150.             REQUEST.scriptDataPtr = kInvalidPageAddress;
  151.             status = NCRSetupTestISR(perRequestDataPtr);
  152.             break;
  153.         case kSCSITestMemoryScript:
  154.             REQUEST.watchdogTimeout = SCSI.watchdogTimeout;
  155.             REQUEST.scriptDataPtr = kInvalidPageAddress;
  156.             status = NCRSetupTestMemory(perRequestDataPtr);
  157.             break;
  158.         default:
  159.             status = paramErr;                    /* Can't happen                    */
  160.             break;
  161.         }
  162.         if (status == noErr) {
  163.             /*
  164.              * We are ready to start an NCR I/O operation. The actual work is
  165.              * done by the Secondary Interrupt Handler.
  166.              */
  167.             status = NCRQueueSecondaryInterrupt(perRequestDataPtr, kIORequestStart);
  168.             if (status == noErr)                /* If successful, we return        */
  169.                 status = kIOBusyStatus;            /* busy to the driver mainline    */
  170.         }
  171.         if (status != kIOBusyStatus) {
  172.             /*
  173.              * We can't start I/O -- make sure the user's buffer is un-prepared.
  174.              */
  175.             CheckStatus(status, "\pNCRStartScript");
  176.             CheckpointIOTable(&REQUEST.scsiIOTable);
  177.         }
  178.         return (status);
  179. }
  180.  
  181. /*
  182.  * Setup a SCSI request. pb->ioMisc points to the NCRSCSIParam structure.
  183.  */
  184. OSErr
  185. NCRSetupDoSCSIScript(
  186.         PerRequestDataPtr        perRequestDataPtr
  187.     )
  188. {
  189.         OSErr                    status;
  190.         ByteCount                scsiCommandLength;
  191.         UInt32                    ncrDeviceID;
  192.  
  193.         Trace(NCRSetupDoSCSIScript);
  194.         /*
  195.          * Check for valid parameters. The caller has already verified that PBRead
  196.          * or PBWrite matches SCSI.driverAction.
  197.          */
  198.         status = noErr;
  199.         PB.ioActCount = 0;
  200. #if 0 && USE_LOG_LIBRARY
  201.         WriteLogEntry(GLOBAL.logRecordPtr, 'StSc',
  202.             LogFormat7(
  203.                 kLogFormatAddress,
  204.                 kLogFormatSigned, kLogFormatSigned, kLogFormatSigned,
  205.                 kLogFormatAddress, kLogFormatUnsigned,
  206.                 kLogFormatAddress
  207.             ),
  208.             &SCSI,
  209.             (signed long) SCSI.targetID,
  210.             (signed long) SCSI.logicalUnitNumber,
  211.             (signed long) SCSI.driverAction,
  212.             PB.ioBuffer, PB.ioReqCount,
  213.             &SCSI.logicalUnitNumber
  214.         );
  215. #endif
  216.         if (SCSI.logicalUnitNumber != 0)            /* This should be ok now    */
  217.             status = scsiLUNInvalid;                /* LUN's are unsupported    */
  218.         else if (SCSI.targetID == GLOBAL.initiatorID || SCSI.targetID >= 8)
  219.             status = scsiTIDInvalid;                /* Invalid target ID        */
  220.         else if ((PB.ioReqCount & 0xFF000000) != 0)    /* Long transfer length        */
  221.             status = scsiRequestInvalid;            /* Longer than SCSI Max        */
  222.         else if (PB.ioBuffer != NULL && PB.ioReqCount == 0) {
  223.             status = scsiRequestInvalid;
  224.         }
  225.         else if (SCSI.driverAction != kNCRDriverNoDataPhase && PB.ioBuffer == NULL) {
  226.             status = paramErr;
  227.         }
  228.         if (status == noErr) {
  229.             scsiCommandLength = SetupSCSICommand(perRequestDataPtr);
  230.             if (scsiCommandLength == 0)                /* Invalid SCSI command?    */
  231.                 status = paramErr;
  232.         }
  233.         /*
  234.          * Here is where we prepare the SCSI and per-request tables for the
  235.          * next request. PrepareNewDMATransfer understands "no data" transfers.
  236.          */
  237.         if (status == noErr)
  238.             status = PrepareNewDMATransfer(perRequestDataPtr);
  239.         if (status == noErr) {
  240.             /*
  241.              * We now have physical addresses for all data, Bind the data area
  242.              * to the script Table elements. All SCSI bus to/from memory
  243.              * accesses are done using the MoveFrom script operator and a
  244.              * static table. This is rather ugly, but the alternative would
  245.              * require patching physical addresses into the script. As done
  246.              * here, the entire script is read-only (after the first call that
  247.              * resolves our hack labels).
  248.              *
  249.              * Note that all addresses that will be read by the NCR chip must
  250.              * be byte-swapped. (Hmm, strictly speaking, this is not quite
  251.              * correct: we can jumper pin 142 (BIT_LIT/) to set the chip into
  252.              * big-endian mode. By running the chip in "PCI-native"
  253.              * little-endian mode, we illustrate the byte-swap requirements.
  254.              */
  255.             ncrDeviceID = (ByteCount)
  256.                 ( (0 << 24)                    /* bytelane 3 SCNTL3 config bits    */
  257.                 | (SCSI.targetID << 16)        /* bytelane 2 Target ID                */
  258.                 | (0 << 8)                    /* bytelane 1 SFXFER Offset/Period    */
  259.                 | 0                            /* bytelane 0 Must be zero            */
  260.                 );
  261.             SCRIPT.idMsgByte = 0x80                    /* Disable disconnect        */
  262.                 | (SCSI.logicalUnitNumber & 0x07);    /* Stuff in the LUN            */
  263.             SCSI.statusByte = SCRIPT.statusByte = 0xFF;
  264.             SCSI.messageByte = SCRIPT.commandCompleteByte = 0xFF;
  265.             SCRIPT.bitBucketOutByte = kScsiMsgAbort; /* For MSGO phase            */
  266.             /*
  267.              * Setup the scatter-gather tables.
  268.              */
  269.             SetTable(deviceIDTable,        0,                            ncrDeviceID);
  270.             SetTable(idMsgTable,        AddressOf(idMsgByte),        1);
  271.             SetTable(commandTable,        AddressOf(scsiCommand),        scsiCommandLength);
  272.             SetTable(statusTable,        AddressOf(statusByte),        1);
  273.             SetTable(completeTable,        AddressOf(commandCompleteByte),    1);
  274.             SetTable(bitBucketInTable,    AddressOf(bitBucketInByte),        1);
  275.             SetTable(bitBucketOutTable,    AddressOf(bitBucketOutByte),    1);
  276.             SetTable(ignoredMsgTable,    AddressOf(ignoredMsgInByte),    1);
  277.         }
  278.         CheckStatus(status, "\pNCRSetupDoSCSIScript failure");
  279.         return (status);
  280. }
  281.  
  282. /*
  283.  * SetupSCSIComand determines the command length and copies the command to the
  284.  * per-request record. It returns 0 if the command length is incorrect.
  285.  */
  286. ByteCount
  287. SetupSCSICommand(
  288.         PerRequestDataPtr        perRequestDataPtr
  289.     )
  290. {
  291.         ByteCount                scsiCommandLength;
  292.  
  293.         Trace(SetupSCSICommand);
  294.         /*
  295.          * We will copy data from the parameter block into the per-request
  296.          * data area. While this simplifies debugging, it has an additional
  297.          * advantage: it cuts down on the number of PrepareMemoryForIO
  298.          * requests to the minimum (script, per-request table, and user data).
  299.          * The only limitation is that it prevents our device from working
  300.          * with a non-standard SCSI device whose command block is longer than
  301.          * 12 bytes.
  302.          */
  303.         scsiCommandLength = SCSI.scsiCommandLength;
  304.         if (scsiCommandLength == 0)
  305.             scsiCommandLength = SCSIGetCommandLength(SCSI.scsiCommand);
  306.         if (scsiCommandLength == 0
  307.          || scsiCommandLength > sizeof SCRIPT.scsiCommand)
  308.             scsiCommandLength = 0;
  309.         if (scsiCommandLength > 0) {
  310.             BlockCopy(SCSI.scsiCommand, SCRIPT.scsiCommand, scsiCommandLength);
  311.             /*
  312.              * Store the LUN information in the command block - this is needed
  313.              * for old devices that only examine the command block for LUN values.
  314.              * (On SCSI-II, the the LUN is stored in the identify message).
  315.              */
  316.             SCRIPT.scsiCommand[1] &= ~0xE0;
  317.             SCRIPT.scsiCommand[1] |= (SCSI.logicalUnitNumber & 0x07) << 5;
  318. #if 1 && USE_LOG_LIBRARY
  319.             {
  320.                 Str255                work;
  321.                 int                    i;
  322.                 
  323.                 work[0] = 0;
  324.                 AppendSigned(work, SCSI.targetID);
  325.                 AppendChar(work, ' ');
  326.                 AppendUnsigned(work,    /* Try to capture the block number    */
  327.                     ( (SCRIPT.scsiCommand[2] << 16)
  328.                     + (SCRIPT.scsiCommand[3] <<  8)
  329.                     + (SCRIPT.scsiCommand[4] <<  0)
  330.                     )
  331.                 );
  332.                 AppendChar(work, ' ');
  333.                 AppendUnsigned(work, PB.ioReqCount);
  334.                 AppendChar(work, ' ');
  335.                 for (i = 0; i < scsiCommandLength; i++) {
  336.                     AppendHexLeadingZeros(work, SCRIPT.scsiCommand[i], 2);
  337.                     if (i < (scsiCommandLength - 1))
  338.                         AppendChar(work, '.');
  339.                 }
  340.                 WriteLogEntry(GLOBAL.logRecordPtr, 'SCSI',
  341.                         LogStringFormat, work);
  342.             }
  343. #endif
  344.         }
  345.         return (scsiCommandLength);
  346. }
  347.  
  348. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  349.  * SCSIGetCommandLength looks at the first command byte. If it's in the ANSI-standard
  350.  * range, return the command length.
  351.  */
  352. ByteCount
  353. SCSIGetCommandLength(
  354.         const unsigned char        *cmdBlock
  355.     )
  356. {
  357.         ByteCount                result;
  358.  
  359.         Trace(SCSIGetCommandLength);
  360.         /*
  361.          * Look at the "group code" in the command operation. Return a parameter
  362.          * error for the reserved (3, 4) and vendor-specific command (6, 7)
  363.          * command groups. Otherwise, set the command length from the group code
  364.          * value as specified in the SCSI-II spec. Then, copy the command block
  365.          * into the parameter block (this centralizes everything for debugging
  366.          * convenience).
  367.          */
  368.         switch (cmdBlock[0] & 0xE0) {
  369.         case (0 << 5):    result = 6;        break;
  370.         case (1 << 5):
  371.         case (2 << 5):    result = 10;    break;
  372.         case (5 << 5):    result = 12;    break;
  373.         default:        result = 0;        break;        /* This results in an error    */
  374.         }
  375.         return (result);
  376. }
  377.  
  378. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  379.  * Setup the ISR test: create a one-instruction script "INT noErr" and return success.
  380.  */
  381. OSErr
  382. NCRSetupTestISR(
  383.         PerRequestDataPtr        perRequestDataPtr
  384.     )
  385. {
  386.         Trace(NCRSetupTestISR);
  387.         PB.ioActCount = 0;
  388.         CLEAR(REQUEST.memoryMoveScript);
  389.         REQUEST.memoryMoveScript[0] = kIntOpcode;
  390.         REQUEST.memoryMoveScript[1] = noErr;
  391.         REQUEST.scriptPtr =
  392.                 ((UInt32) REQUEST.perRequestIOTable.physicalMapping[0])
  393.                 + offsetof(PerRequestData, memoryMoveScript);
  394.         REQUEST.scriptBaseAddress = REQUEST.scriptPtr;
  395.         return (noErr);
  396. }
  397.  
  398. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  399.  * Setup the memory test: Make sure we have the necessary buffers, setup the memory
  400.  * move script, then PrepareMemoryForIO.
  401.  */
  402. OSErr
  403. NCRSetupTestMemory(
  404.         PerRequestDataPtr        perRequestDataPtr
  405.     )
  406. {
  407.         OSErr                    status;
  408.         
  409.         Trace(NCRSetupTestMemory);
  410.         PB.ioActCount = 0;
  411.         status = noErr;
  412.         if (PB.ioBuffer == NULL || SCSI.memTestPhysAddress == NULL)
  413.             status = paramErr;
  414.         if (status == noErr)
  415.             status = PrepareNewDMATransfer(perRequestDataPtr);
  416.         if (status == noErr)    /* TEMP TEMP TEMP TEMP */
  417.             status = CheckForContiguousPhysicalMapping(&REQUEST.perRequestIOTable);
  418.         if (status == noErr) {
  419.             REQUEST.scriptPtr =
  420.                     ((UInt32) REQUEST.perRequestIOTable.physicalMapping[0])
  421.                     + offsetof(PerRequestData, memoryMoveScript);
  422.             REQUEST.scriptBaseAddress = REQUEST.scriptPtr;
  423.         }
  424.         CheckStatus(status, "\pNCRSetupTestMemory");
  425.         return (status);
  426. }
  427.  
  428.